﻿using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Mvc.Controllers;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.OpenApi.Models;
using RuoVea.ExConfig;
using Swashbuckle.AspNetCore.Filters;
using Swashbuckle.AspNetCore.SwaggerGen;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;

namespace RuoVea.ExSwagger;

/// <summary>
/// 
/// </summary>
public static class SwaggerStep
{
    /// <summary>
    /// 
    /// </summary>
    /// <param name="services"></param>
    /// <param name="versions"></param>
    /// <returns></returns>
    /// <exception cref="ArgumentNullException"></exception>
    public static IServiceCollection AddSwaggerServer(this IServiceCollection services, List<DocVersions> versions)
    {
        if (services == null) throw new ArgumentNullException(nameof(services));

        services.AddSwaggerGen(options =>
        {
            string contactName = Appsettings.GetSection("SwaggerDoc:ContactName").Value;
            string contactNameEmail = Appsettings.GetSection("SwaggerDoc:ContactEmail").Value;
            string contactUrl = Appsettings.GetSection("SwaggerDoc:ContactUrl").Value;
            string termsOfService = Appsettings.GetSection("SwaggerDoc:TermsOfService").Value;

            if (versions != null && versions.Count > 0)
            {
                foreach (var v in versions)
                {
                    options.SwaggerDoc(v.Version, new OpenApiInfo()
                    {
                        Title = $"{v.Title} -- {Appsettings.GetSection("SwaggerDoc:Title").Value} - {RuntimeInformation.FrameworkDescription}",
                        Version = v.Version,
                        TermsOfService = new Uri(termsOfService),
                        Description = Appsettings.GetSection("SwaggerDoc:Description").Value,
                        Contact = new OpenApiContact { Name = contactName, Email = contactNameEmail, Url = new Uri(contactUrl) },
                    }
                    );
                    options.AddServer(new OpenApiServer()
                    {
                        Url = contactUrl,
                        Description = $"{v.Title} -- {Appsettings.GetSection("SwaggerDoc:Title").Value} - {RuntimeInformation.FrameworkDescription}"
                    });
                }
            }

            options.CustomOperationIds(apiDesc =>
            {
                var controllerAction = apiDesc.ActionDescriptor as ControllerActionDescriptor;
                return controllerAction.ControllerName + "-" + controllerAction.ActionName;
            });
            options.DocumentFilter<SwaggerApi>();

            #region Security
            //给api添加token令牌证书
            options.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme
            //options.AddSecurityDefinition("Authorization", new OpenApiSecurityScheme
            {
                //Id = "Bearer",
                Type = SecuritySchemeType.Http,
                Name = "Authorization",
                Description = "JWT Authorization header using the Bearer scheme.",
                BearerFormat = "JWT",
                Scheme = "bearer",
                In = ParameterLocation.Header,
            });
            //添加安全请求
            options.AddSecurityRequirement(new OpenApiSecurityRequirement {{
                    new OpenApiSecurityScheme{
                        Reference =new  OpenApiReference{ Type=ReferenceType.SecurityScheme, Id="Bearer" }
                    }, new string[]{ }
                }
                });
            #endregion

            //options.DocumentFilter<CustomDocumentFilter>();

            options.OrderActionsBy((apidesc) => apidesc.RelativePath);
            ////向swagger添加header参数
            //options.OperationFilter<AddRequiredHeaderParameter>();
            //开启加权锁
            options.OperationFilter<AddResponseHeadersFilter>();
            options.OperationFilter<AppendAuthorizeToSummaryOperationFilter>();
            // 在header中添加token，传递到后台
            options.OperationFilter<SecurityRequirementsOperationFilter>();
            options.OrderActionsBy((apiDesc) => $"{apiDesc.ActionDescriptor.RouteValues["controller"]}");

            try
            {
                Directory.GetFiles(AppDomain.CurrentDomain.BaseDirectory, "*.xml").ToList().ForEach(file =>
                {
                    options.IncludeXmlComments(file, true);
                });
            }
            catch { }
        });
        return services;
    }

    /// <summary>
    /// 
    /// </summary>
    /// <param name="app"></param>
    public static void AddSwaggerApp(this WebApplication app, List<DocVersions> versions, bool myHtml = false)
    {
        if (app == null) throw new ArgumentNullException(nameof(app));

        app.UseSwagger();
        // 配置 Swagger UI 参数
        app.UseSwaggerUI(options =>
        {
            // 配置文档标题
            options.DocumentTitle = $"{Appsettings.GetSection("SwaggerDoc:Title").Value}";// "RuoVea 接口文档";
                                                                                          //// 配置UI地址（处理二级虚拟目录）
                                                                                          //options.RoutePrefix = "doc";
            if (versions != null && versions.Count > 0)
                foreach (var v in versions)
                    options.SwaggerEndpoint("/swagger/" + v.Version + "/swagger.json", v.Title);
            // 文档展开设置
            options.DocExpansion(Swashbuckle.AspNetCore.SwaggerUI.DocExpansion.None);
            if (myHtml)
            {
                var xmlPath = Path.Combine(AppContext.BaseDirectory, "index.html");//index-mini-profiler
                FileStream fileStream = new FileStream(xmlPath, FileMode.Open);
                options.IndexStream = (Func<Stream>)(() => fileStream);
                // 配置多语言和自动登录token
                options.UseRequestInterceptor("(request) => { return defaultRequestInterceptor(request); }");
                options.UseResponseInterceptor("(response) => { return defaultResponseInterceptor(response); }");
            }
        });

        //// UI 和 Swagger配置一样一样滴
        //app.UseFytApiUI(options =>
        //{
        //    options.DocumentTitle = $"{Appsettings.GetSection("SwaggerDoc:Title").Value}";// "RuoVea 接口文档";
        //    options.ConfigObject = new FytApi.MUI.ConfigObject { DocExpansion = FytApi.MUI.DocExpansion.None };// 文档展开设置
        //    foreach (var v in ApiVersions.version)
        //        options.SwaggerEndpoint("/swagger/" + v.Version + "/swagger.json", v.Title, v.Version);
        //});

        //app.UseKnife4UI(c =>
        //{
        //    c.RoutePrefix = "doc"; // serve the UI at root    //c.SwaggerEndpoint("/v1/swagger.json", "V1 Docs");
        //    foreach (var v in ApiVersions.version)
        //        c.SwaggerEndpoint($"/{v.Version}/swagger.json", v.Title);
        //});

        // 增加支持静态资源访问 
        app.UseStaticFiles();
    }
}

#region 自定义文档过滤器
/// <summary>
/// 自定义文档过滤器
/// </summary>
public class CustomDocumentFilter : IDocumentFilter
{
    /// <summary>
    /// 
    /// </summary>
    /// <param name="swaggerDoc">文档</param>
    /// <param name="context"></param>
    public void Apply(OpenApiDocument swaggerDoc, DocumentFilterContext context)
    {

        //多版本接口名修正
        var match = new OpenApiPaths();
        foreach (var apiDesc in context.ApiDescriptions)
        {
            var key = "/" + apiDesc.RelativePath;
            if (!swaggerDoc.Paths.ContainsKey(key)) continue;//swaggerDoc.paths是当前选择版本的接口，例：v1

            ControllerActionDescriptor actionDescriptor = (ControllerActionDescriptor)apiDesc.ActionDescriptor;
            var controllerName = actionDescriptor.ControllerName;
            var actionName = actionDescriptor.ActionName;
            if (!string.IsNullOrEmpty(controllerName) && !string.IsNullOrEmpty(actionName))
            {
                var t = Type.GetType(actionDescriptor.ControllerTypeInfo.Namespace + "." + controllerName);
                if (t != null)
                {
                    var baseControllerName = t.GetMethod(actionName).DeclaringType.Name;
                    if (controllerName != baseControllerName)
                    {
                        if (key.Contains("?"))
                            key = key.Substring(0, key.IndexOf("?", StringComparison.Ordinal));
                        swaggerDoc.Paths.Remove(key);//移除继承的Action，避免文档中重复展示
                    }
                }
            }

            var path = swaggerDoc.Paths.FirstOrDefault(o => o.Key == key);
            var lsXG = path.Key.Split('/');
            var index = Array.IndexOf(lsXG, controllerName);
            string vc = lsXG.FirstOrDefault<string>(controllerName);
            var lsXXG = lsXG[index].Split('_');
            if (lsXXG.Count() == 4)
            {
                match.Add(path.Key.Replace(lsXG[index], lsXXG[0]) + "?version=v" + lsXXG[1] + "." + lsXXG[2] + "." + lsXXG[3], path.Value);
            }
            else
            {
                if (!match.Select(x => x.Key).Contains(path.Key))
                    match.Add(path.Key, path.Value);
            }

        }
        swaggerDoc.Paths = match;
    }
}
#endregion


